#include "mod_gpio.h"
#include "aexgpio.h"
#include "kc031service.h"

static void msleep(int ms) {
	if(ms < 10)ms = 10;
    struct timespec ts = {
        .tv_sec = ms / 1000,
        .tv_nsec = (ms % 1000) * 1000000
    };
    nanosleep(&ts, NULL);
}

#ifdef SGWEC_BOARD

unsigned int F_AEXIN_MIN_INDEX()
{
	return 0;		//输入的最小序号
}

unsigned int F_AEXIN_MAX_INDEX()
{
	return 15;		//输入的最大序号
}

unsigned int F_AEXOUT_MIN_INDEX()
{
	return 16;		//输出的最小序号
}

unsigned int F_AEXOUT_MAX_INDEX()
{
	return 23;		//输出的最大序号
}

unsigned int F_NUM_AEXGPIO_IN()
{
	return aexgpio_get_dinum(DEFAULT_GPIO_ADDR);	//8个IO输入
}

unsigned int F_NUM_AEXGPIO_OUT()
{
	return aexgpio_get_donum(DEFAULT_GPIO_ADDR);	//8个IO输出
}

unsigned int F_DEFAULT_GPIO_ADDR()
{
	return 0x27;			//KC031 GPIO芯片的I2C地址
}

static inline unsigned long fs_read(char *fname)
{
	int fd;
	unsigned long ret=0;
	char buf[128];
	
	/* open file */
	fd = open(fname, O_RDONLY);

	if (fd <= 0) {
		psus_error("%s: file %s open error\n", __FUNCTION__, fname);
		return 0;
	}
	/* read data */
	memset(buf,0,sizeof(buf));
	if(read(fd,buf,sizeof(buf)) > 0){
		ret = StrToInt(buf);
	}
	// printf("fs_read :%s",buf);
	/* read data out successfully */
	close(fd);
	return ret;
}

static inline void fs_write(char *fname,unsigned long value)
{
	int fd;
	char buf[128];
	
	/* open file */
	fd = open(fname, O_RDWR);

	if (fd <= 0) {
		//printf("%s: file %s open error\n", __FUNCTION__, fname);
		return ;
	}
	/* read data */
	memset(buf,0,sizeof(buf));
	sprintf(buf,"0x%X",value);
	write(fd,buf,strlen(buf));
	/* read data out successfully */
	close(fd);
}
/**
 * 边缘计算获取模块插槽地址
 */
static inline unsigned int aexgpio_get_module_addr()
{
	char fs[128];
	int ret = 0;
	
	memset(fs,0,sizeof(fs));
	sprintf(fs,AEXGPIO_FS,"value");
	ret = fs_read(fs);	
	return (ret&0xF0)|SGW_TYPE_EC;
}

/**
 * 读取板载GPIO的值
 */
static inline unsigned int aexgpio_onboard_get(char *gname)
{
	char fs[128];
	unsigned int ret = 0;
	
	memset(fs,0,sizeof(fs));
	sprintf(fs,AEXGPIO_ONBOARD_ADDRESS,gname);
	ret = fs_read(fs);	
	return ret>0?1:0;
}

/**
 * 设置板载GPIO的值
 */
static inline void aexgpio_onboard_set(char *gname,unsigned int value)
{
	char fs[128];

	memset(fs,0,sizeof(fs));
	sprintf(fs,AEXGPIO_ONBOARD_ADDRESS,gname);
	fs_write(fs,value);
}

/*
| 15   | 14   | 13   | 12   | 11   | 10   | 09   | 08   | 07    | 06    | 05 | 04 | 03 | 02 | 01 | 00 |
| ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ----- | ----- | -- | -- | -- | -- | -- | -- |
| 总线 | 在线 | 错误 | 保留 | 保留 | 保留 | 保留 | 保留 | 保留 | 保留 | 保留 | 保留 | 保留 | 保留 | 保留 | 保留 |
| I    | I    | I    | X    | X    | X    | X    | X    | X    | X   | X | X | X  | X | X  | X  |
*/
unsigned long sgw_get_in()
{
	unsigned long ret=0;

	ret |= aexgpio_onboard_get(SGWEC_LED_BUS)<<15;
	ret |= aexgpio_onboard_get(SGWEC_LED_ONLINE)<<14;
	ret |= aexgpio_onboard_get(SGWEC_LED_ERROR)<<13;
	return ret;
}

/**
| 07    | 06   | 05 | 04 | 03 | 02 | 01 | 00 |
| ----- | ---- | -- | -- | -- | -- | -- | -- |
| 保留 | RS232/485EN| U1 |  U2 | 保留 | 保留 | 保留 | 保留 |
| O     | O   | O  | O  | X | X  | X  | X  |

1:RS232
0:RS485
 */
unsigned long sgw_get_out()
{
	unsigned long ret=0;

	ret |= 0<<7;
	ret |= aexgpio_onboard_get(SGWEC_RS485_PWRSEL)<<6;
	ret |= aexgpio_onboard_get(SGWEC_CH1_SEL)<<5;
	ret |= aexgpio_onboard_get(SGWEC_CH2_SEL)<<4;
	return ret;
}

/*
| 07    | 06   | 05 | 04 | 03 | 02 | 01 | 00 |
| ----- | ---- | -- | -- | -- | -- | -- | -- |
| 232EN | 485EN| U1  |  U2 | U3 | U4 | U5 | U6 |
| O     | O     | O  | O  | O  | O  | O  | O  |
*/
void sgw_set_out(unsigned char index,unsigned char level)
{
	switch (index)
	{
	case 6:		//485EN(PB0)
		aexgpio_onboard_set(SGWEC_RS485_PWRSEL,level);
		break;
	case 5:	//SEL1(PE12)
		aexgpio_onboard_set(SGWEC_CH1_SEL,level);
		break;
	case 4:		//SEL2(PE11)
		aexgpio_onboard_set(SGWEC_CH2_SEL,level);
		break;
	default:
		break;
	}
}

/**
 * 模块GPIO相关的初始化函数
 */
void aexgpio_init()
{
	aexgpio_set_bus_status(0);
	aexgpio_set_online_status(0);
	aexgpio_set_error_status(0);
	psus_hint("%s:mod addr=0x02X,value=0x%08X",__func__,aexgpio_get_mod_addr(),aexgpio_getvalue(DEFAULT_GPIO_ADDR));
}

/**
 * aexgpio根据地址获取IO数值的函数，此函数需要各个模块根据模块设计来实现。
 * 
 * param 
 * 	address : GPIO的地址，可以是I2C地址，也可以是IO组序号，默认是0
 */
unsigned long aexgpio_getvalue(int address)
{
	unsigned long value = (sgw_get_in()<<AEXIN_MIN_INDEX) | (sgw_get_out()<<AEXOUT_MIN_INDEX);
	// psus_hint("%s:sgw_get_in=%X,sgw_get_out=%X",__func__,sgw_get_in(),sgw_get_out());
	// psus_hint("%s:value=%X",__func__,value);
	return value;
}

/**
 * aexgpio根据地址设置IO数值的函数，DO就是通过这个函数来设置输出状态的，此函数需要各个模块根据IO模块设计来实现。
 * param
 *   address : GPIO的地址，可以是I2C地址，也可以是IO组序号，默认是0
 *   value : 要设置的数值。
 */
void aexgpio_setvalue(int address,unsigned long value)
{
	int i;

	for(i=0;i<aexgpio_get_donum(DEFAULT_GPIO_ADDR);i++){
		int level = value&(1<<(i+AEXOUT_MIN_INDEX));
		sgw_set_out(i,level?1:0);		
	}
}

/**
 * 模块GPIO获取模块地址的函数，如果没有地址的概念则可以返回0。
 */
unsigned char aexgpio_get_mod_addr()
{
	return aexgpio_get_module_addr();		//KC031也是一种边缘计算网关
}

/**
 * 设置RS485总线状态
 */
void aexgpio_set_bus_status(int level)
{
	aexgpio_onboard_set(SGWEC_LED_BUS,level);
}

/**
 * 设置设备在线状态
 */
void aexgpio_set_online_status(int level)
{
	aexgpio_onboard_set(SGWEC_LED_ONLINE,level);
}

/**
 * 设置设备错误状态
 */
void aexgpio_set_error_status(int level)
{
	aexgpio_onboard_set(SGWEC_LED_ERROR,level);
}

/**
 * 模块GPIO获取DI的数量
 */
unsigned char aexgpio_get_dinum(unsigned char addr)
{
	return 16;		//KC031包含8路开关量输入
}

/**
 * 模块GPIO获取DO的数量
 */
unsigned char aexgpio_get_donum(unsigned char addr)
{
	return 8;		//KC031包含8路开关量输出
}

/**
 * 模块GPIO获取AI的数量
 */
unsigned char aexgpio_get_ainum(unsigned char addr)
{
	return 0;		//KC031不包含模拟输入
}
/**
 * 模块GPIO获取AI的数值，最大支持32位AD
 */
unsigned long aexgpio_get_ai(unsigned char addr,int index)
{
	return 0;		//KC031不包含模拟输入，返回0
}

/**
 * 模块GPIO获取AO的数量
 */
unsigned char aexgpio_get_aonum(unsigned char addr)
{
	return 0;		//KC031不包含模拟输出
}

/**
 * 模块GPIO获取保持寄存器AO的数值，最大支持32位DA
 */
unsigned long aexgpio_get_ao(unsigned char addr,int index)
{
	return 0;		//KC031不包含模拟输出
}

/**
 * 模块GPIO设置保持寄存器AO的值，最大支持32位DA
 */
void aexgpio_set_ao(unsigned char addr,int index,unsigned long value)
{
	//KC031 不包含模拟输出
}

/**
 * KC031的RS485输出控制
 */
void modgpio_setrs485out(unsigned int value)
{
	aexgpio_onboard_set("ecint",!value);
	psus_hint("ecint %s",!value?"pull up":"pull down");
}

/**
 * KC031检查输入的函数，最新版本使用了中断来处理，不需要当期检查输入了
 */
unsigned int modgpio_chkinput()
{
	return 0;
}

/**
 * sgw-ec控制底板的RS485输出
 */
void modgpio_rs485bus_ctrl(unsigned int value)
{
	aexgpio_onboard_set("rs485bus",value);
}

int modgpio_waitfor_rs485bus(int busy,int delays)
{
	int ret = aexgpio_onboard_get("rs485bus");
	
	while ( ret != busy && delays > 0)
	{
		usleep(1000);
		delays--;
		ret = aexgpio_onboard_get("rs485bus");
	}
	return ret;
}

/**
 * 底板RS485总线写数据的函数，首先对总线信号拉高，等待发送完成后拉低。
 */
int modgpio_rs485_write(int fd,char *buf,int len)
{
	int ret = 0;
	
	clock_t exectime = clock();

	if(modgpio_waitfor_rs485bus(0,100) == 0){
		modgpio_setrs485out(1);
		modgpio_waitfor_rs485bus(1,100);
		ret = write(fd,buf,len);
		msleep(((len*10000)/get_baudrate(fd))+1);
		modgpio_setrs485out(0);
		exectime = clock() - exectime;
		psus_hint("%s:%d run %d us clock.",__func__,__LINE__,(exectime*1000000)/CLOCKS_PER_SEC);
	}else{
		psus_hint("not wait for rs485 to 0.");
	}
	return ret;
}

#else

unsigned int F_AEXIN_MIN_INDEX()
{
	return 0;		//输入的最小序号
}

unsigned int F_AEXIN_MAX_INDEX()
{
	return 7;		//输入的最大序号
}

unsigned int F_AEXOUT_MIN_INDEX()
{
	return 8;		//输出的最小序号
}

unsigned int F_AEXOUT_MAX_INDEX()
{
	return 15;		//输出的最大序号
}

unsigned int F_NUM_AEXGPIO_IN()
{
	return aexgpio_get_dinum(DEFAULT_GPIO_ADDR);	//8个IO输入
}

unsigned int F_NUM_AEXGPIO_OUT()
{
	return aexgpio_get_donum(DEFAULT_GPIO_ADDR);	//8个IO输出
}

unsigned int F_DEFAULT_GPIO_ADDR()
{
	return 0x27;			//KC031 GPIO芯片的I2C地址
}

static inline unsigned long fs_read(char *fname)
{
	int fd;
	unsigned long ret=0;
	char buf[128];
	
	/* open file */
	fd = open(fname, O_RDONLY);

	if (fd <= 0) {
		//printf("%s: file %s open error\n", __FUNCTION__, fname);
		return -1;
	}
	/* read data */
	memset(buf,0,sizeof(buf));
	if(read(fd,buf,sizeof(buf)) > 0){
		ret = StrToInt(buf);
	}
	//printf("fs_read :%s",buf);
	/* read data out successfully */
	close(fd);
	return ret;
}

static inline void fs_write(char *fname,unsigned long value)
{
	int fd;
	char buf[128];
	
	/* open file */
	fd = open(fname, O_RDWR);

	if (fd <= 0) {
		//printf("%s: file %s open error\n", __FUNCTION__, fname);
		return ;
	}
	/* read data */
	memset(buf,0,sizeof(buf));
	sprintf(buf,"0x%X",value);
	write(fd,buf,strlen(buf));
	/* read data out successfully */
	close(fd);
}

static inline unsigned int modgpio_getconfig(int address)
{
	char fs[128];
	char buf[128];
	int ret = 0;
	
	memset(buf,0,sizeof(buf));
	memset(fs,0,sizeof(fs));
	sprintf(fs,AEXGPIO_FS,address,"config");
	ret = fs_read(fs);	
	return ret;
}

static inline void modgpio_setconfig(int address,unsigned int value)
{
	char fs[128];
	
	memset(fs,0,sizeof(fs));
	sprintf(fs,AEXGPIO_FS,address,"config");
	fs_write(fs,value);
}

static inline unsigned int modgpio_getinvert(int address)
{
	char fs[128];
	char buf[128];
	int ret = 0;
	
	memset(buf,0,sizeof(buf));
	memset(fs,0,sizeof(fs));
	sprintf(fs,AEXGPIO_FS,address,"invert");
	ret = fs_read(fs);	
	return ret;
}

static inline void modgpio_setinvert(int address,unsigned int value)
{
	char fs[128];
	
	memset(fs,0,sizeof(fs));
	sprintf(fs,AEXGPIO_FS,address,"invert");
	fs_write(fs,value);
}

/**
 * 模块GPIO相关的初始化函数
 */
void aexgpio_init()
{
	aexgpio_set_bus_status(0);
	aexgpio_set_online_status(0);
	aexgpio_set_error_status(0);
	aexgpio_onboard_set("ecint",1);
	psus_hint("%s:config=0x%08X,value=0x%08X",__func__,modgpio_getconfig(DEFAULT_GPIO_ADDR),aexgpio_getvalue(DEFAULT_GPIO_ADDR));
}

/**
 * aexgpio根据地址获取IO数值的函数，此函数需要各个模块根据模块设计来实现。
 * 
 * param 
 * 	address : GPIO的地址，可以是I2C地址，也可以是IO组序号，默认是0
 */
unsigned long aexgpio_getvalue(int address)
{
	char fs[128];
	unsigned long ret = 0;
	
	memset(fs,0,sizeof(fs));
	sprintf(fs,AEXGPIO_FS,address,"value");
	ret = fs_read(fs);	
	return ret;
}

/**
 * aexgpio根据地址设置IO数值的函数，DO就是通过这个函数来设置输出状态的，此函数需要各个模块根据IO模块设计来实现。
 * param
 *   address : GPIO的地址，可以是I2C地址，也可以是IO组序号，默认是0
 *   value : 要设置的数值。
 */
void aexgpio_setvalue(int address,unsigned long value)
{
	char fs[128];

	memset(fs,0,sizeof(fs));
	sprintf(fs,AEXGPIO_FS,address,"value");
	fs_write(fs,value);
}

/**
 * 模块GPIO获取模块地址的函数，如果没有地址的概念则可以返回0。
 */
unsigned char aexgpio_get_mod_addr()
{
	return SGW_TYPE_EC;		//KC031也是一种边缘计算网关
}

void aexgpio_set_bus_status(int level)
{
}

void aexgpio_set_online_status(int level)
{
}

void aexgpio_set_error_status(int level)
{
}

/**
 * 模块GPIO获取DI的数量
 */
unsigned char aexgpio_get_dinum(unsigned char addr)
{
	return 8;		//KC031包含8路开关量输入
}

/**
 * 模块GPIO获取DO的数量
 */
unsigned char aexgpio_get_donum(unsigned char addr)
{
	return 8;		//KC031包含8路开关量输出
}

/**
 * 模块GPIO获取AI的数量
 */
unsigned char aexgpio_get_ainum(unsigned char addr)
{
	return 0;		//KC031不包含模拟输入
}
/**
 * 模块GPIO获取AI的数值，最大支持32位AD
 */
unsigned long aexgpio_get_ai(unsigned char addr,int index)
{
	return 0;		//KC031不包含模拟输入，返回0
}

/**
 * 模块GPIO获取AO的数量
 */
unsigned char aexgpio_get_aonum(unsigned char addr)
{
	return 0;		//KC031不包含模拟输出
}

/**
 * 模块GPIO获取保持寄存器AO的数值，最大支持32位DA
 */
unsigned long aexgpio_get_ao(unsigned char addr,int index)
{
	return 0;		//KC031不包含模拟输出
}

/**
 * 模块GPIO设置保持寄存器AO的值，最大支持32位DA
 */
void aexgpio_set_ao(unsigned char addr,int index,unsigned long value)
{
	//KC031 不包含模拟输出
}

/**
 * KC031的RS485输出控制
 */
void modgpio_setrs485out(unsigned int value)
{
	char fs[128];
	
	memset(fs,0,sizeof(fs));
	sprintf(fs,AEXGPIO_FS,DEFAULT_GPIO_ADDR,"rs485out");
	fs_write(fs,value);
}

/**
 * KC031检查输入的函数，最新版本使用了中断来处理，不需要当期检查输入了
 */
unsigned int modgpio_chkinput()
{
	char fs[128];
	unsigned long ret = 0;
	
	memset(fs,0,sizeof(fs));
	sprintf(fs,AEXGPIO_FS,DEFAULT_GPIO_ADDR,"chkinput");
	ret = fs_read(fs);	
	return ret;
}

/**
 * RS485写数据的函数，等待发送完成后切换为接收状态
 */
int modgpio_rs485_write(int fd,char *buf,int len)
{
	int ret = 0;

	modgpio_setrs485out(1);
	ret = write(fd,buf,len);
	msleep((len*10000/get_baudrate(fd))+1);
	modgpio_setrs485out(0);
	return ret;
}

#endif